package yassp.client;

import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PipedInputStream;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import yassp.Code;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
/**
 *
 * @author Milridor
 */
public class Folder {

    public Folder(String path) throws IOException, NoSuchAlgorithmException {
        this.path = path;
        String[] tmp = path.split(java.io.File.separator);
        this.name = tmp[tmp.length - 1];
        updateFromFolder();
    }

    public void updateFromFolder() throws IOException, NoSuchAlgorithmException {
        java.io.File f = new java.io.File(path);
        if (!f.exists()) {
            return;
        }
        this.lastUpdated = f.lastModified();

        folderMap.keySet().retainAll(Arrays.asList(f.list()));

        for (Folder content : folderMap.values()) {
            java.io.File cont = new java.io.File(content.path);
            if (content.lastUpdated < cont.lastModified()) {
                if (cont.isDirectory()) {
                    content.updateFromFolder();
                } else {
                    folderMap.remove(content.name);
                    newFileOrFolder(content.name);
                }
            }
        }

        fileMap.keySet().retainAll(Arrays.asList(f.list()));
        for (FileDesc content : fileMap.values()) {
            java.io.File cont = new java.io.File(path, content.name);
            if (content.lastModified < cont.lastModified()) {
                fileMap.remove(content.name);
                newFileOrFolder(content.name);
            }

        }

        List<String> newFilesAndFolders = Arrays.asList(f.list());
        newFilesAndFolders.removeAll(folderMap.keySet());
        newFilesAndFolders.removeAll(fileMap.keySet());

        for (String tmp : newFilesAndFolders) {
            newFileOrFolder(tmp);
        }


    }

    public void updateFromBlob(String blob, String path) throws IOException, NoSuchAlgorithmException {

        //TODO folder and file removal
        String[] content = blob.split("\n\n", 2);

        this.lastUpdated = Long.decode(content[0].split(":")[0]);
        String[] folders = content[1].split("\n");
        String[] files = content[2].split("\n");
        boolean pushNecessary = false;

        for (String tmp : folders) {
            Long timestamp = Long.decode(tmp.split(":", 2)[0]);
            String folder = tmp.split(":", 2)[1];
            boolean toUpdate = false;
            Folder f = folderMap.get(folder);
            if (f == null) {
                f = new Folder(this.path + java.io.File.pathSeparator + folder);
                folderMap.put(folder, f);
                toUpdate = true;
            }
            if (f.lastUpdated != timestamp) {
                toUpdate = true;
                if(f.lastUpdated > timestamp)
                    pushNecessary = true;
            }

            if (toUpdate) {
                Command c = new Command(Code.PULLFOLDER,path+"/"+f.name);
                ClientApp.conn.pushCommand(c);
            }

        }

        for (String tmp : files) {
            Long timestamp = Long.decode(tmp.split(":", 3)[0]);
            String file = tmp.split(":", 3)[1];
            String hash = tmp.split(":", 3)[2];
            boolean toUpdate = false;
            boolean toPush = false;
            FileDesc f = fileMap.get(file);
            if (f == null) {
                f = new FileDesc(file, timestamp, hash);
                fileMap.put(file, f);
                toUpdate = true;
            }
            if (f.lastModified < timestamp) {
                toUpdate = true;
            } else {
                if(f.lastModified > timestamp)
                    toPush = true;
            }

            if (toUpdate) {
                Command c = new Command(Code.PULLFILE,f.hash);
                ClientApp.conn.pushCommand(c);
            }
            else
                if(toPush) {
                    Command c = new Command(Code.PUSHFILE,f.hash);
                    ClientApp.conn.pushCommand(c);
                }
        }
        
        if(pushNecessary)
        {
            Command c = new Command(Code.PUSHFOLDER,path);
            ClientApp.conn.pushCommand(c);
        }


    }

    /*
     * Serialize format:
     * modificationdate:path
     * 
     * folderdate:foldername
     * ...
     * folderdate:foldername
     * 
     * filedate:filehash:filename
     * ...
     * filedate:filehash:filename
     */
    public String serialize(String path) {
        String ret = Long.toString(lastUpdated) + ":" + path + "/" + name + "\n\n";
        for (Folder f : folderMap.values()) {
            ret += (Long.toString(f.lastUpdated) + ":" + f.name + "\n");
        }
        ret += "\n";
        for (FileDesc f : fileMap.values()) {
            ret += (Long.toString(f.lastModified) + ":" + f.hash + ":" + f.name + "\n");
        }
        return ret;
    }

    public void deserialize(DataInputStream dis, String path, String destPath) throws IOException, NoSuchAlgorithmException {
        if (destPath.equals(path)) {
            String s = "";
            s = dis.readUTF();
            updateFromBlob(s,path);
        } else {
            String remainingPath = destPath.substring(path.length());
            String childName = remainingPath.split("/")[0];
            Folder child = folderMap.get(childName);
            if (child == null) {
                newFolder(childName);
                child = folderMap.get(remainingPath.split("/")[0]);
            }
            child.deserialize(dis, path + this.name + "/", destPath);
        }
    }

    private void newFileOrFolder(String name) throws IOException, NoSuchAlgorithmException {
        java.io.File f = new java.io.File(path, name);
        if (f.isDirectory()) {
            newFolder(name);
        } else {
            newFile(name);
        }
    }

    private void newFolder(String name) throws IOException, NoSuchAlgorithmException {
        folderMap.put(name, new Folder(this.path + java.io.File.pathSeparator + name));
    }

    private void newFile(String name) throws IOException, NoSuchAlgorithmException {
        java.io.File f = new java.io.File(path, name);
        String hash = File.makeHash(f.getPath());
        if (!FileStore.hashExist(hash)) {
            FileStore.insert(new File(f.getPath(), hash));
        }

        fileMap.put(name, new FileDesc(name, f.lastModified(), hash));
    }

    Set<String> getHashesList() {
        Set<String> result = null;

        for (Folder f : folderMap.values()) {
            result.addAll(f.getHashesList());
        }

        for (FileDesc fd : fileMap.values()) {
            result.add(fd.hash);
        }

        return result;
    }

    public void write() {
        java.io.File f = new java.io.File(this.path);
        if (!f.exists()) {
            f.mkdir();
        }
        for (FileDesc fd : fileMap.values()) {
            File file = FileStore.get(fd.hash);
            if (file != null) {
                try {
                    file.write(this.path + java.io.File.pathSeparator + fd.name);
                } catch (FileNotFoundException ex) {
                    Logger.getLogger(Folder.class.getName()).log(Level.SEVERE, null, ex);
                } catch (IOException ex) {
                    Logger.getLogger(Folder.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }

        for (Folder fold : folderMap.values()) {
            fold.write();
        }
    }
    Map<String, Folder> folderMap;
    Map<String, FileDesc> fileMap;
    String path;
    String name;
    long lastUpdated;
}
